/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 * Description: Cast Session implement realization.
 * Create: 2024
 */

#include "cast_session_wifi_display.h"
#include "mirror_player_impl.h"

#include <sstream>
#include "utils.h"
#include "permission.h"

namespace OHOS {
namespace CastEngine {
namespace CastEngineService {
DEFINE_CAST_ENGINE_LABEL("Cast-SessionWifiDisplay");

class WifiDisplayStateListener : public IWifiDisplayStateListener {
public:
    explicit WifiDisplayStateListener(CastSessionWifiDisplay* sessionWifiDisplay)
        :sessionWifiDisplay_(sessionWifiDisplay)
    {
    }

    virtual ~WifiDisplayStateListener() = default;
    void OnConnectionChanged(DeviceState state, const std::string &deviceId, const std::string &deviceName) override
    {
        if (sessionWifiDisplay_) {
            sessionWifiDisplay_->OnConnectionChanged(state, deviceId, deviceName);
        }
    }
    void OnConnectionError(DeviceState state, const std::string &deviceId, int32_t errCode) override
    {
        if (sessionWifiDisplay_) {
            sessionWifiDisplay_->OnConnectionError(state, deviceId, errCode);
        }
    }

private:
    CastSessionWifiDisplay *sessionWifiDisplay_ = nullptr;
};

CastSessionWifiDisplay::CastSessionWfdEventListener::CastSessionWfdEventListener(CastSessionWifiDisplay *sessionWifiDisplay)
    : IWfdEventListener(), sessionWifiDisplay_(sessionWifiDisplay)
{
}

void CastSessionWifiDisplay::CastSessionWfdEventListener::OnInfo(std::shared_ptr<Sharing::BaseMsg> &msg)
{
    CHECK_AND_RETURN_LOG(msg != nullptr, "msg is nullptr.");

    CLOGI("msg: %{public}d.", msg->GetMsgId());

    using namespace OHOS::Sharing;

    switch (msg->GetMsgId()) {
        case Sharing::WfdConnectionChangedMsg::MSG_ID: {
            auto data = std::static_pointer_cast<Sharing::WfdConnectionChangedMsg>(msg);
            CLOGI("wfd connection changed, device state %{public}d", data->state);

            switch (data->state) {
                case Sharing::ConnectionState::CONNECTED: {
                    CastRemoteDevice remote = {
                        .deviceId = data->mac,
                        .deviceName = data->deviceName,
                        .ipAddress = data->ip,
                        .sessionId = sessionWifiDisplay_->sessionId_,
                    };
                    std::vector<CastRemoteDevice> deviceList { remote };
                    sessionWifiDisplay_->OnDeviceRequest(deviceList);
                    break;
                }
                case Sharing::ConnectionState::DISCONNECTED: {
                    sessionWifiDisplay_->OnConnectionChanged(DeviceState::DISCONNECTED, data->mac, data->deviceName);
                    break;
                }
                default: {
                    CLOGI("Unhandled state.");
                    break;
                }
            }

            break;
        }
        default:
            CLOGI("none process case.");
            break;
    }
}

CastSessionWifiDisplay::CastSessionWifiDisplay(const CastSessionProperty &property, const CastLocalDevice &localDevice)
    : localDevice_(localDevice), property_(property)
{
    CLOGI("In");
}

CastSessionWifiDisplay::~CastSessionWifiDisplay()
{
    CLOGD("~CastSessionWifiDisplay");
    WifiDisplayManager::GetInstance().UnregisterStateListener();
}

bool CastSessionWifiDisplay::Init(int32_t sessionId)
{
    sessionId_ = sessionId;
    WifiDisplayManager::GetInstance().RegisterStateListener(std::make_shared<WifiDisplayStateListener>(this));

    if (property_.protocolType == ProtocolType::MIRACAST && property_.endType == EndType::CAST_SINK) {
        wfdEventListener_ = std::make_shared<CastSessionWfdEventListener>(this);
        bool ret = WifiDisplayManager::GetInstance().RegisterSinkEventListener(wfdEventListener_);
        if (ret != 0) {
            CLOGE("SetListener failed");
            return false;
        }
    }

    return true;
}

void CastSessionWifiDisplay::SetServiceCallbackForRelease(const std::function<void(int)> &callback)
{
    std::lock_guard<std::mutex> lock(serviceCallbackMutex_);
    serviceCallback_ = callback;
}

int32_t CastSessionWifiDisplay::RegisterListener(sptr<ICastSessionListenerImpl> listener)
{
    CLOGI("Start to register session listener");
    std::unique_lock<std::mutex> lock(mutex_);
    listeners_[IPCSkeleton::GetCallingPid()] = listener;
    uicastAppUid_ = GetCallingUid();
    CLOGI("uicast app uid: %d", uicastAppUid_);
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::UnregisterListener()
{
    std::unique_lock<std::mutex> lock(mutex_);
    listeners_.erase(IPCSkeleton::GetCallingPid());
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::AddDevice(const CastRemoteDevice &remoteDevice)
{
    CLOGI("AddDevice");

    CastInnerRemoteDevice remote = {
        .deviceId = remoteDevice.deviceId, .deviceName = remoteDevice.deviceName,
        .deviceType = remoteDevice.deviceType, .subDeviceType = remoteDevice.subDeviceType,
        .ipAddress = remoteDevice.ipAddress, .sessionId = sessionId_,
        .channelType = remoteDevice.channelType, .networkId = remoteDevice.networkId,
        .isLeagacy = remoteDevice.isLeagacy
    };

    return AddDeviceInner(remote) ? CAST_ENGINE_SUCCESS : CAST_ENGINE_ERROR;
}

bool CastSessionWifiDisplay::AddDeviceInner(const CastInnerRemoteDevice &remoteDevice)
{
    if (!AddRemoteDevice(CastRemoteDeviceInfo { remoteDevice, DeviceState::DISCONNECTED })) {
        return false;
    }
    CLOGI("AddDevice");
    WifiDisplayManager::GetInstance().AddCastDevice(remoteDevice.deviceId);
    return true;
}

int32_t CastSessionWifiDisplay::RemoveDevice(const std::string &deviceId)
{
    CLOGI("RemoveDevice");
    RemoveRemoteDevice(deviceId);

    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::CreateMirrorPlayer(sptr<IMirrorPlayerImpl> &mirrorPlayer)
{
    CLOGI("CastSessionWifiDisplay CreateMirrorPlayer");

    std::lock_guard<std::mutex> lock(mutex_);
    mirrorPlayer_ = sptr<MirrorPlayerImpl>::MakeSptr(this);
    mirrorPlayer = mirrorPlayer_;

    return CAST_ENGINE_SUCCESS;
}

sptr<IMirrorPlayerImpl> CastSessionWifiDisplay::MirrorPlayerGetter()
{
    std::lock_guard<std::mutex> lock(mutex_);
    return mirrorPlayer_;
}

int32_t CastSessionWifiDisplay::CreateStreamPlayer(sptr<IStreamPlayerIpc> &streamPlayer)
{
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::Release()
{
    std::lock_guard<std::mutex> lock(mutex_);
    for (auto it = remoteDeviceList_.begin(); it != remoteDeviceList_.end(); it++) {
        CLOGI("Start to remove remote device:%s", it->remoteDevice.deviceId.c_str());
        WifiDisplayManager::GetInstance().RemoveCastDevice(it->remoteDevice.deviceId);
    }
    remoteDeviceList_.clear();

    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::StartAuth(const AuthInfo &authInfo)
{
    static_cast<void>(authInfo);
    return CAST_ENGINE_ERROR;
}

int32_t CastSessionWifiDisplay::GetSessionId(std::string &sessionId)
{
    std::lock_guard<std::mutex> lock(mutex_);
    sessionId = std::to_string(sessionId_);
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::GetDeviceState(const std::string &deviceId, DeviceState &deviceState)
{
    auto deviceInfo = FindRemoteDevice(deviceId);
    DeviceState state = DeviceState::DISCONNECTED;
    if (deviceInfo != nullptr) {
        state = deviceInfo->deviceState;
    }
    CLOGI("device state: %{public}s", DEVICE_STATE_STRING[static_cast<int>(state)].c_str());
    deviceState = state;
    return CAST_ENGINE_SUCCESS;
}

std::shared_ptr<CastRemoteDeviceInfo> CastSessionWifiDisplay::FindRemoteDevice(const std::string &deviceId)
{
    std::lock_guard<std::mutex> lock(mutex_);
    return FindRemoteDeviceLocked(deviceId);
}

std::shared_ptr<CastRemoteDeviceInfo> CastSessionWifiDisplay::FindRemoteDeviceLocked(const std::string &deviceId)
{
    for (auto &deviceInfo : remoteDeviceList_) {
        if (deviceInfo.remoteDevice.deviceId == deviceId) {
            return std::make_shared<CastRemoteDeviceInfo>(deviceInfo);
        }
    }
    return nullptr;
}

void CastSessionWifiDisplay::RemoveRemoteDevice(const std::string &deviceId)
{
    std::lock_guard<std::mutex> lock(mutex_);
    for (auto it = remoteDeviceList_.begin(); it != remoteDeviceList_.end(); it++) {
        if (it->remoteDevice.deviceId == deviceId) {
            CLOGI("Start to remove remote device:%{public}s", Utils::Mask(deviceId).c_str());
            remoteDeviceList_.erase(it);
            WifiDisplayManager::GetInstance().RemoveCastDevice(deviceId);
            return;
        }
    }
}

bool CastSessionWifiDisplay::AddRemoteDevice(const CastRemoteDeviceInfo &remoteDeviceInfo)
{
    const auto &remote = remoteDeviceInfo.remoteDevice;
    if (FindRemoteDevice(remote.deviceId) != nullptr) {
        CLOGW("Remote device(%s) has existed", Utils::Mask(remote.deviceName).c_str());
        return false;
    }
    std::lock_guard<std::mutex> lock(mutex_);
    remoteDeviceList_.push_back(remoteDeviceInfo);
    return true;
}

void CastSessionWifiDisplay::UpdateRemoteDeviceId(const std::string &deviceId, const std::string &deviceName)
{
    std::lock_guard<std::mutex> lock(mutex_);
    for (auto it = remoteDeviceList_.begin(); it != remoteDeviceList_.end(); it++) {
        if (it->remoteDevice.deviceName == deviceName && it->remoteDevice.deviceId != deviceId) {
            it->remoteDevice.deviceId = deviceId;
        }
    }
}

int32_t CastSessionWifiDisplay::GetSessionProtocolType(ProtocolType &protocolType)
{
    CLOGI("GetSessionProtocolType in");
    std::unique_lock <std::mutex> lock(mutex_);
    protocolType = property_.protocolType;
    return CAST_ENGINE_SUCCESS;
}

void CastSessionWifiDisplay::SetSessionProtocolType(ProtocolType protocolType)
{
    CLOGI("SetSessionProtocolType in %d old %d", protocolType, property_.protocolType);
    std::unique_lock <std::mutex> lock(mutex_);
    property_.protocolType = protocolType;
}

int32_t CastSessionWifiDisplay::SetSessionProperty(const CastSessionProperty &property)
{
    property_ = property;
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::NotifyEvent(EventId eventId, std::string &jsonParam)
{
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::SetCastMode(CastMode mode, std::string &jsonParam)
{
    CLOGD("in, mode = %d, param = %s", static_cast<int>(mode), jsonParam.c_str());
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::SetAudioOutput(EndType type)
{
    CLOGD("in, mode = %d,", static_cast<int>(type));
    WifiDisplayManager::GetInstance().SetAudioOutput(type);
    return CAST_ENGINE_SUCCESS;
}

template<class T>
std::string CastSessionWifiDisplay::CreateParamter(const std::string &Key, T value)
{
    std::stringstream sstream;
    sstream << Key << "=" << value;
    return sstream.str();
}

int32_t CastSessionWifiDisplay::SetCastRoute(const bool &remote)
{
    std::string str = CreateParamter(KEY_AUDIO, remote);
    CLOGI("SetCastRoute remote = %s", str.c_str());

    WifiDisplayManager::GetInstance().SetParamter(str);
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::Play(const std::string &deviceId)
{
    return WifiDisplayManager::GetInstance().Play(deviceId) ? CAST_ENGINE_SUCCESS : CAST_ENGINE_ERROR;
}

int32_t CastSessionWifiDisplay::Pause(const std::string &deviceId)
{
    return WifiDisplayManager::GetInstance().Pause(deviceId) ? CAST_ENGINE_SUCCESS : CAST_ENGINE_ERROR;
}

int32_t CastSessionWifiDisplay::SetSurface(sptr<IBufferProducer> producer)
{
    std::lock_guard<std::mutex> lock(mutex_);

    if (acceptDeviceId_.empty()) {
        CLOGE("acceptDeviceId_ is empty");
        return CAST_ENGINE_ERROR;
    }

    bool ret = WifiDisplayManager::GetInstance().SetSurface(acceptDeviceId_, producer);
    if (ret) {
        surfaceId_ = producer->GetUniqueId();
    }

    return ret ? CAST_ENGINE_SUCCESS : CAST_ENGINE_ERROR;
}

int32_t CastSessionWifiDisplay::DestroyMirrorPlayer()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if(!mirrorPlayer_) {
        return CAST_ENGINE_ERROR;
    }

    if (!acceptDeviceId_.empty() && !WifiDisplayManager::GetInstance().Close(acceptDeviceId_)) {
        return CAST_ENGINE_ERROR;
    }

    if (surfaceId_ != 0 && !WifiDisplayManager::GetInstance().RemoveSurface(acceptDeviceId_, surfaceId_)) {
        return CAST_ENGINE_ERROR;
    }

    surfaceId_ = 0;
    mirrorPlayer_ = nullptr;
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::GetDisplayId(std::string &displayId)
{
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::ResizeVirtualScreen(uint32_t width, uint32_t height)
{
    if (!Permission::CheckPidPermission()) {
        return ERR_NO_PERMISSION;
    }
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::DeliverInputEvent(const OHRemoteControlEvent &event)
{
    return CAST_ENGINE_SUCCESS;
}

int32_t CastSessionWifiDisplay::InjectEvent(const OHRemoteControlEvent &event)
{
    return CAST_ENGINE_SUCCESS;
}

void CastSessionWifiDisplay::OnConnectionChanged(DeviceState state, const std::string &deviceId, const std::string &deviceName)
{
    UpdateRemoteDeviceId(deviceId, deviceName);
    for (const auto &[pid, listener] : listeners_) {
        listener->OnDeviceState(DeviceStateInfo { state, deviceId, ReasonCode::REASON_DEFAULT});
    }
}
void CastSessionWifiDisplay::OnConnectionError(DeviceState state, const std::string &deviceId, int32_t errCode)
{
    for (const auto &[pid, listener] : listeners_) {
        listener->OnDeviceState(DeviceStateInfo { state, deviceId, ReasonCode::REASON_DEFAULT});
    }
}

void CastSessionWifiDisplay::OnDeviceRequest(const std::vector<CastRemoteDevice> &deviceList)
{
    mutex_.lock();
    std::map<pid_t, sptr<ICastSessionListenerImpl>> listener(listeners_);
    mutex_.unlock();

    for (const auto &[pid, listener] : listener) {
        listener->OnDeviceRequest(deviceList);
    }
}

int32_t CastSessionWifiDisplay::AcceptDeviceConnection(const std::string& deviceId, bool accept)
{
    bool result = WifiDisplayManager::GetInstance().AcceptDeviceConnection(deviceId, accept);
    if(result) {
        acceptDeviceId_ = accept ? deviceId : "";
    }
    return result ? CAST_ENGINE_SUCCESS : CAST_ENGINE_ERROR;
}

} // namespace CastEngineService
} // namespace CastEngine
} // namespace OHOS
